package top.dyzmj.detty.core.utils;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import static top.dyzmj.detty.core.utils.ObjectUtil.checkNotNull;

/**
 * 常数池
 *
 * @author dongYu
 * @date 2021/11/19
 */
public abstract class ConstantPool<T extends Constant<T>> {

	private final ConcurrentHashMap<String, T> constants = new ConcurrentHashMap<>();

	private final AtomicInteger nextId = new AtomicInteger(1);

	public T valueOf(Class<?> firstNameComponent, String secondNameComponent) {
		return valueOf(checkNotNull(firstNameComponent, "firstNameComponent").getName() + '#'
				+ checkNotNull(secondNameComponent, "secondNameComponent"));
	}

	/**
	 * 回赋给指定名称的常量。
	 * 如果没有这样的常量，将创建一个新的常量并返回。
	 * 一旦创建，同名的后续调用将总是返回之前创建的那个(即singleton)。
	 *
	 * @param name 常量的名字
	 */
	public T valueOf(String name) {
		return getOrCreate(checkNotNull(name, "name"));
	}

	/**
	 * 如果给定名称存在AttributeKey则返回true。
	 */
	public boolean exists(String name) {
		return constants.containsKey(checkNotNull(name, "name"));
	}

	/**
	 * 为给定的名称创建一个新的常量，如果存在给定名称的常量，则以 {@link IllegalArgumentException} 失败
	 */
	public T newInstance(String name) {
		return createOrThrow(checkNotNull(name, "name"));
	}

	/**
	 * 根据名称获取现有常量，如果不存在则创建新的常量。线程安全的
	 *
	 * @param name 常量的名字
	 * @return 常量
	 */
	private T getOrCreate(String name) {
		T constant = constants.get(name);
		if (Objects.isNull(constant)) {
			final T tempConstant = newConstant(nextId(), name);
			constant = constants.putIfAbsent(name, tempConstant);
			if (Objects.isNull(constant)) {
				return tempConstant;
			}
		}
		return constant;
	}

	/**
	 * 按名称创建常量或抛出异常。线程安全的
	 *
	 * @param name 常量的名字
	 */
	private T createOrThrow(String name) {
		T constant = constants.get(name);
		if (Objects.isNull(constant)) {
			final T tempConstant = newConstant(nextId(), name);
			constant = constants.putIfAbsent(name, tempConstant);
			if (Objects.isNull(constant)) {
				return tempConstant;
			}
		}
		throw new IllegalArgumentException(String.format("'%s' is already in use", name));
	}

	/**
	 * 创建新的常量
	 */
	protected abstract T newConstant(int id, String name);

	/**
	 * 获取下一个常量编号
	 */
	public final int nextId() {
		return nextId.getAndIncrement();
	}
}
